package team.seekers.hula.island.island.util;

import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalTime;
import java.util.Collections;
import java.util.Optional;

/**
 * @author GH
 */
@Component
@Slf4j
public class PublicIslandPermission {

    /**
     * 刻入的记忆时间点集合的Redis KEY
     */
    private static final String GENERATE_TIME_KEY = "public_island_post_limiter.generate_times";
    /**
     * 定时刻入记忆时间点的分布式锁的Redis KEY
     */
    private static final String SCHEDULE_LOCK_KEY = "public_island_post_limiter.lock";
    /**
     * 定时刻入记忆时间点的分布式锁的锁存在时间
     */
    private static final Duration LOCK_EXPIRE_TIME = Duration.ofMinutes(5);

    @Value("${hula.island.generate-max-number}")
    private Integer maxNumber;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public Boolean hasAuthority() {
        return limitAlgorithm();
    }

    private Boolean limitAlgorithm() {
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("scripts/public_island_post_limiter.lua")));
        redisScript.setResultType(Boolean.class);
        return Optional.ofNullable(this.redisTemplate.execute(redisScript,
                        Collections.singletonList(GENERATE_TIME_KEY), String.valueOf(LocalTime.now().toSecondOfDay())))
                .orElse(false);
    }

    /**
     * 每天0点初始化记忆岛屿刻入的随机时间点
     */
    @Scheduled(cron = "0 0 0 * * ?")
    private void generateTimes() {
        try {
            // 集群下只有获取锁成功的节点才进行初始化时间点，过期时间保证其他节点定时任务已执行完毕
            if (Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(SCHEDULE_LOCK_KEY, "Lock", LOCK_EXPIRE_TIME))) {
                String[] addTimes = new String[maxNumber];
                // 每次刻入记忆的间隔时间（秒）
                int intervalTime = 24 * 60 * 60 / maxNumber;
                int timePoint = 0;
                for (int i = 0; i < maxNumber; i++) {
                    addTimes[i] = String.valueOf(timePoint + RandomUtil.randomInt(intervalTime));
                    timePoint += intervalTime;
                }
                redisTemplate.delete(GENERATE_TIME_KEY);
                redisTemplate.opsForList().rightPushAll(GENERATE_TIME_KEY, addTimes);
            }
        } catch (Exception e) {
            log.error("定时任务初始化随机时间到redis失败", e);
        }
    }
}
